package org.albino.evinreisi.facebook;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.albino.evinreisi.facebook.FacebookSession.State;
import org.apache.commons.lang.StringUtils;

public class FacebookAuthFilter implements Filter {
	// Constants
	final String applicationId = SystemProperties.get("facebook.application.id");
	final String canvasBaseUrl = SystemProperties.get("facebook.canvas.base.url");
	final String applicationUrl = SystemProperties.get("facebook.application.url");
	final String landingUrl = SystemProperties.get("facebook.landing.url");
	final String secretKey = SystemProperties.get("facebook.secret.key");
	final String permissions = SystemProperties.get("facebook.permissions");

	public void init(FilterConfig arg0) throws ServletException {

	}

	public void destroy() {

	}

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		FacebookSession session = null;

		if (!StringUtils.isEmpty(request.getParameter("mobileToken"))) {
			// log.finest("Mobile token is set, means this is a mobile client and already has a token.");
			String mobileToken = request.getParameter("mobileToken");

			session = FacebookSession.buildFacebookSession(mobileToken);
			storeSession(session, request);

			// finish and return the session. because if you keep on going, then
			// the mobile client will be redirected to oauth. not good.
			chain.doFilter(req, res);
			return;
		}

		session = getSession(request);

		if (!StringUtils.isEmpty(request.getParameter("forceLogin"))) {
			session = null;
		}

		if (session == null) {
			session = FacebookSession.buildFacebookSession();
			storeSession(session, request);
		}

		if (session.isConnected()) {
			chain.doFilter(req, res);
			return;
		}

		if (session.isConnecting()) {
			String token = readToken(request.getParameter("code"));

			System.out.println("tok = " + token);

			session = FacebookSession.buildFacebookSession(token);
			storeSession(session, request);

			if (session.isDisconnected()) {
				// log.severe("redirecting to try again.");
				// TODO check if safeRedirect necessary
				safeRedirect(response, landingUrl);
				return;
			}

			// redirect to home screen
			safeRedirect(response, applicationUrl);
			return;
		}

		// session.setState(State.DISCONNECTED);
		// storeSession(session, request);

		if (session.isDisconnected()) {
			session.setState(State.CONNECTING);
			storeSession(session, request);

			String oAuthUrl = SystemProperties.get("facebook.oAuth.url", permissions, applicationId, landingUrl);

			// TODO check if safeRedirect necessary
			safeRedirect(response, oAuthUrl);
			return;
		}

		// everything is cool...proceed to servlet
		chain.doFilter(req, res);
	}

	private void safeRedirect(HttpServletResponse response, String url) throws IOException {
		redirect(response, SystemProperties.get("redirecter.url", url));
	}

	private void redirect(HttpServletResponse response, String url) throws IOException {
		response.sendRedirect(url);
	}

	private String urlEncode(String url) {
		try {
			return URLEncoder.encode(url, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	private String readToken(String code) {
		// log.finest("readToken is called.");
		// log.finest("code = " + code);

		if (code == null) {
			// log.severe("Code is null!");
			return null;
		}

		String accessTokenUrl = SystemProperties.get("facebook.accessToken.url", applicationId, secretKey, code, landingUrl);

		try {
			String token = Util.readUrlIntoString(accessTokenUrl).split("&")[0].replaceFirst("access_token=", "");
			// log.finest("token is fetched. token = " + token);
			return token;
		} catch (IOException e) {
			// log.log(Level.SEVERE, "IOException occured while reading url",
			// e);
			return null;
		} catch (Exception e) {
			// log.log(Level.SEVERE, "Exception occured while reading url", e);
			return null;
		}
	}

	private FacebookSession getSession(HttpServletRequest request) {
		return (FacebookSession) request.getSession().getAttribute("facebookSession");
	}

	private void storeSession(FacebookSession session, HttpServletRequest request) {
		request.getSession().setAttribute("facebookSession", session);
	}
}
